//
//  View+Gradation.swift
//  TTCOM-iOS
//
//  Created by Toj on 8/12/22.
//

import Foundation
import UIKit

public enum GADirection: Int {
    case horizontal // - 水平方向, 从正左到正右
    case vertical   // | 垂直方向, 从正上到正下
    case slash      // / 正斜方向, 从左下到右上
    case backslash  // \ 反斜方向, 从左上到右下
    public typealias GAPoint = (startPoint: CGPoint, endPoint: CGPoint)
    
    public var point: GAPoint {
        switch self {
        case .horizontal:
            return (startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 1, y: 0))
        case .vertical:
            return (startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0, y: 1))
        case .slash:
            return (startPoint: CGPoint(x: 0, y: 1), endPoint: CGPoint(x: 1, y: 0))
        case .backslash:
            return (startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 1, y: 1))
        }
    }
}

private extension TTView {
    
    /**
     添加渐变色
     - parameter icolors:    内层 渐变色
     - parameter idirection: 内层 渐变色 方向
     
     - parameter ocolors:    外层 渐变色 颜色
     - parameter odirection: 外层 渐变色 方向
     - parameter owidth:     外层 渐变色 高度
     
     - parameter radius: 圆角
     */
    func gd_layer(
        innerColors icolors: [CGColor],
        innerDirection idirection: GADirection,
        
        outerColors ocolors: [CGColor] = [],
        outerDirection odirection: GADirection,
        outerWidth owidth: CGFloat = 0,
        
        corner radius: CGFloat = 0
    ) -> CALayer? {
        var innerColors = icolors
        var outerColors = ocolors
        if outerColors.isEmpty {
            outerColors = innerColors
            innerColors = []
        }
        
        // 渐变 外图层
        guard let outerLayer = setupGradientLayer(colors: outerColors, direction: odirection) else {
            return nil
        }
        outerLayer.frame = bounds
        if radius > 0 { outerLayer.cornerRadius = radius }
        
        // 渐变 内图层
        if let innerLayer = setupGradientLayer(colors: innerColors, direction: idirection) {
            /** 边框 圆角 图层 */
            let xy = owidth
            let width = bounds.width - owidth * 2
            let height = bounds.height - owidth * 2
            
            innerLayer.frame = CGRect(x: xy, y: xy, width: width, height: height)
            if radius > 0 { innerLayer.cornerRadius = radius - owidth }
            outerLayer.addSublayer(innerLayer)
        }
        
        return outerLayer
    }
    
    /**
     创建渐变色图层
     - parameter colors: 渐变颜色
     - parameter direction: 渐变方向
     */
    func setupGradientLayer(
        colors: [CGColor],
        direction: GADirection
    ) -> CALayer? {
        guard !colors.isEmpty else { return nil }
        
        guard colors.count > 1 else {
            let _layer = CALayer()
            _layer.backgroundColor = colors.first
            return _layer
        }
        
        // 渐变 外图层
        let _layer = CAGradientLayer()
        
        // 颜色的分界点
        let locations = gradientLocations(color: colors.count)
        _layer.locations = locations
        // 多大区域
        // _layer.frame = bounds
        // 渐变值
        _layer.colors = colors
        // 控制渐变方向
        _layer.startPoint = direction.point.startPoint
        _layer.endPoint  = direction.point.endPoint
        
        return _layer
    }
    
    /**
     平均分配颜色段
     - parameter ccount: 颜色数量
     */
    func gradientLocations(color ccount: Int) -> [NSNumber] {
        // 颜色的分界点
        if ccount <= 2 { return [0, 1] }
        
        var locations:[NSNumber] = [] // [0.1, 0.45, 1]
        var sum = 0.0
        let medianCount = ccount - 1 // 最后是1
        let interval = 1 / Double(medianCount)
        for _ in 0..<medianCount {
            locations.append(NSNumber(value: sum))
            sum += interval
        }
        locations.append(NSNumber(value: 1))
        return locations
    }
}

private var runtime_contents_layer_key = "gradient_contents_layer_key"
public extension TTView {
    
    func gd_add(
        colors: [String],
        direction: GADirection = .horizontal,
        corner radius: CGFloat = 0,
        cornerMask mask: CACornerMask = .ca_layerAllCorner,
        borderColor bcolor: CGColor? = nil,
        borderWidth bwidth: CGFloat = 1
    ) {
        var cls: [CGColor] = []
        colors.forEach { cls.append($0.to_color.cgColor) }
        gd_add(
            colors: cls,
            direction: direction,
            corner: radius,
            cornerMask: mask,
            borderColor: bcolor,
            borderWidth: bwidth
        )
    }
    
    func gd_add(
        colors: [UIColor],
        direction: GADirection = .horizontal,
        corner radius: CGFloat = 0,
        cornerMask mask: CACornerMask = .ca_layerAllCorner,
        borderColor bcolor: CGColor? = nil,
        borderWidth bwidth: CGFloat = 1
    ) {
        var cls: [CGColor] = []
        colors.forEach { cls.append($0.cgColor) }
        gd_add(
            colors: cls,
            direction: direction,
            corner: radius,
            cornerMask: mask,
            borderColor: bcolor,
            borderWidth: bwidth
        )
    }
    
    /**
     添加渐变色
     - parameter colors:    内层 渐变色
     - parameter direction: 内层 渐变色 方向
     - parameter radius:    圆角
     */
    func gd_add(
        colors: [CGColor],
        direction: GADirection = .horizontal,
        corner radius: CGFloat = 0,
        cornerMask mask: CACornerMask = .ca_layerAllCorner,
        borderColor bcolor: CGColor? = nil,
        borderWidth bwidth: CGFloat = 1
    ) {
        // 渐变 外图层
        guard let _layer = setupGradientLayer(colors: colors, direction: direction) else {
            gd_remove()
            return
        }
        _layer.frame = bounds
        if radius > 0 {
            _layer.cornerRadius = radius
            _layer.maskedCorners = mask
        }
        
        if bcolor != nil {
            let borderLayer = CALayer()
            borderLayer.frame = bounds
            borderLayer.backgroundColor = UIColor.clear.cgColor
            borderLayer.borderColor = bcolor
            borderLayer.borderWidth = bwidth
            if radius > 0 {
                borderLayer.cornerRadius = radius
                borderLayer.maskedCorners = mask
            }
            _layer.addSublayer(borderLayer)
        }
        
        backgroundColor = .clear
        
        // 渲染器渲染上下文图层生成图片
        let renderer = UIGraphicsImageRenderer(size: bounds.size)
        let image = renderer.image { context in
            _layer.render(in: context.cgContext)
        }
        
        let associatedValue = objc_getAssociatedObject(self, &runtime_contents_layer_key)
        let contentsLayer = (associatedValue as? CALayer) ?? CALayer()
        contentsLayer.contents = image.cgImage
        contentsLayer.frame = bounds
        
        guard associatedValue == nil else { return }
        layer.insertSublayer(contentsLayer, at: 0)
        objc_setAssociatedObject(
            self, &runtime_contents_layer_key,
            contentsLayer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
        )
    }
    
    /**
     添加渐变色
     - parameter icolors:    内层 渐变色
     - parameter idirection: 内层 渐变色 方向
     
     - parameter ocolors:    外层 渐变色 颜色
     - parameter odirection: 外层 渐变色 方向
     - parameter owidth:     外层 渐变色 高度
     
     - parameter radius: 圆角
     */
    func gd_add(
        innerColors icolors: [String],
        innerDirection idirection: GADirection = .horizontal,
        
        outerColors ocolors: [String] = [],
        outerDirection odirection: GADirection = .vertical,
        outerWidth owidth: CGFloat = 0,
        
        corner radius: CGFloat = 0
    ){
        var icls: [CGColor] = []
        icolors.forEach { icls.append($0.to_color.cgColor) }
        
        var ocls: [CGColor] = []
        ocolors.forEach { ocls.append($0.to_color.cgColor) }
        gd_add(
            innerColors: icls,
            innerDirection: idirection,
            outerColors: ocls,
            outerDirection: odirection,
            outerWidth: owidth,
            corner: radius
        )
    }
    
    /**
     添加渐变色
     - parameter icolors:    内层 渐变色
     - parameter idirection: 内层 渐变色 方向
     
     - parameter ocolors:    外层 渐变色 颜色
     - parameter odirection: 外层 渐变色 方向
     - parameter owidth:     外层 渐变色 高度
     
     - parameter radius: 圆角
     */
    func gd_add(
        innerColors icolors: [UIColor],
        innerDirection idirection: GADirection = .horizontal,
        
        outerColors ocolors: [UIColor] = [],
        outerDirection odirection: GADirection = .vertical,
        outerWidth owidth: CGFloat = 0,
        
        corner radius: CGFloat = 0
    ){
        var icls: [CGColor] = []
        icolors.forEach { icls.append($0.cgColor) }
        
        var ocls: [CGColor] = []
        ocolors.forEach { ocls.append($0.cgColor) }
        gd_add(
            innerColors: icls,
            innerDirection: idirection,
            outerColors: ocls,
            outerDirection: odirection,
            outerWidth: owidth,
            corner: radius
        )
    }
    
    /**
     添加渐变色
     - parameter icolors:    内层 渐变色
     - parameter idirection: 内层 渐变色 方向
     
     - parameter ocolors:    外层 渐变色 颜色
     - parameter odirection: 外层 渐变色 方向
     - parameter owidth:     外层 渐变色 高度
     
     - parameter radius: 圆角
     */
    func gd_add(
        innerColors icolors: [CGColor],
        innerDirection idirection: GADirection = .horizontal,
        
        outerColors ocolors: [CGColor] = [],
        outerDirection odirection: GADirection = .vertical,
        outerWidth owidth: CGFloat = 0,
        
        corner radius: CGFloat = 0
    ){
        let _layer = gd_layer(innerColors: icolors, innerDirection: idirection, outerColors: ocolors, outerDirection: odirection, outerWidth: owidth, corner: radius)
        guard let outerLayer = _layer else {
            gd_remove()
            return
        }
        
        backgroundColor = .clear
        
        // 渲染器渲染上下文图层生成图片
        let renderer = UIGraphicsImageRenderer(size: bounds.size)
        let image = renderer.image { context in
            outerLayer.render(in: context.cgContext)
        }
        
        let associatedValue = objc_getAssociatedObject(self, &runtime_contents_layer_key)
        let contentsLayer = (associatedValue as? CALayer) ?? CALayer()
        contentsLayer.contents = image.cgImage
        contentsLayer.frame = bounds
        
        guard associatedValue == nil else { return }
        layer.insertSublayer(contentsLayer, at: 0)
        objc_setAssociatedObject(
            self, &runtime_contents_layer_key,
            contentsLayer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
        )
    }
    
    /**
     移除渐变色
     */
    func gd_remove() {
        // 查看是否有缓存值
        guard let associatedObjectValue = objc_getAssociatedObject(self, &runtime_contents_layer_key),
              let contentsLayer = associatedObjectValue as? CALayer else { return }
        contentsLayer.removeFromSuperlayer()
        
        // 清除对象内部值
        objc_setAssociatedObject(
            self, &runtime_contents_layer_key,
            nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
        )
    }
}
